/**
 * datapack 模块
 * 包含与数据相关的组件.
 * FG.DataUnit: 基础的数据单元.
 * FG.DataPack: 对应数据接口中单个json文件, 是多个DataUnit的聚合体.
 * FG.PackManager: 管理从data_briefs得到的datapack信息,
 *                    并将取来的数据json文件解包, 重新组织安排, 并对外提供键值对形式的接口.
 * FG.DataBundle: 根据上层应用需要重新打包的一组DataUnits, 供FigPane绘图用.
 *                与DataPack的区别是DataPack与取数据接口相关联.
 * FG.FigPane: 基本的数据展示单元, 如一个HighCharts图, 一个风玫瑰图等
 * FG.FigCombo: 由若干FigPane组成的复合图表, 并管理其布局. 最终提供给widget展示区域的即FigCombo.
 */

var FG;
if (!FG)
    FG = {};

/**
 * FG.DataUnit是最基本的数据单元, 其成员中的src是所出自的ProductManager.
 * 参数: options对象, 其必须成员为uri, 可选成员为src(若不存在则用默认的ProductManager)
 * 成员函数: getData(), 返回从其src取来的数据延迟(dfd)对象.
 * @param options
 * @constructor
 */
FG.DataUnit = function(options) {
    // 完成.
    this.uri = options.uri;
    this.src = (!!options.src) ? options.src : FG.getPackManager();
    var this_ = this;

    this.getData = function() {
        return this_.src.getData(this_.uri);
    };
};

/**
 * FG.ProductBundle是对应于服务器所提供的json数据包的类型.
 * 构造函数的参数对象中应有 uri 一项.
 * TODO: 添加与数据大小相关的信息.
 */
FG.DataPack = Backbone.Model.extend({
    defaults: function() {
        return {
        }
    },

    initialize: function() {
        this.uri = this.get('uri');
        this.url = '/data/' + this.uri;
        this._normalUrl = this.url;
        this._reloadUrl = this.url + '?ignore_cache=True';
        // toc(目录)用来记录本bundle所含的数据uri记录, 以及其是否存在. 以便在bundle的数据被抛弃时, 仍然可以获得bundle所含内容.
        this.toc = {};
        this.fetching = false;
        this.manager = FG.getPackManager();
    },

    parse: function(rawData) {
        var this_ = this;
        var processedData = _.pick(rawData, 'desc');
        var emptyCommon = {info: {}, coords: {}, data: {}},
            common = (typeof rawData.common === 'undefined') ? emptyCommon : _.extend(emptyCommon, rawData.common);

        processedData.contents = {};
        _.each(rawData.contents, function(entry, key, list) {
            var fullEntry = {};
            _.each(['info', 'coords', 'data'], function(field){
                if (_.has(entry, field)) {
                    fullEntry[field] = _.extend(_.clone(common[field]), entry[field])
                } else {
                    fullEntry[field] = _.clone(common[field])
                }
            });
            fullEntry.uri = rawData.tags[key];
            processedData.contents[fullEntry.uri] = fullEntry;
            this_.toc[fullEntry.uri] = 'exist';
        });
        this.manager.addPack(this_);
        return processedData;
    },

    // 供外部调用, 更新toc信息.
    pushTOC: function(entryURI, status) {
        if (typeof status === 'undefined') {
            if (!_.has(this.toc, entryURI)) {
                this.toc[entryURI] = 'unknown'
            }
        } else {
            this.toc[entryURI] = status;
        }
    },

    dropData: function() {
        // todo: the right way to delete this.contents;
        this.hasData = false;
        this.dfd = undefined;
    },

    getData: function(uri) {
        var this_ = this;
        var unitDfd = $.Deferred();
        if (this_.hasData) {
            unitDfd.resolve(this_.get('contents')[uri]);
        } else {
            if (!this.dfd) {
                this.dfd = $.Deferred();
            }
            this.dfd.done(function(contents) {
                unitDfd.resolve(contents[uri])
            });
            if (!this.fetching) {
                this.fetching = true;
                this_.fetch({
                    success: function() {
                        this_.hasData = true;
                        this_.dfd.resolve(this_.get('contents'));
                        this_.fetching = false;
                    }
                })
            }
        }
        return unitDfd;
    },

    reload: function(opts) {
        var this_ = this;
        if (!this.fetching) {
            this.fetching = true;
            this.url = this._reloadUrl;
            this.fetch({
                success: function(model, response, options) {
                    this_.url = this_._normalUrl;
                    this_.fetching = false;
                    if (!!opts && !!opts.success){
                        opts.success(model, response, options);
                    }
                },
                error: function (model, response, options) {
                    this_.url = this_._normalUrl;
                    this_.fetching = false;
                    if (!!opts && !!opts.error){
                        opts.error(model, response, options);
                    }
                }
            })
        }
    },

    contentSize: function() {}
});

/**
 * FG.Dataset用来记录数据集的信息, 特别是变量存储结构(schema)
 */
FG.Dataset = Backbone.Model.extend({
    urlRoot: '/api/fg/dataset',

    defaults: function() {
        return {
            "schema": '{}'
        }
    },

    initialize: function(){
        this.defaultData = {};
    },

    parse: function(response) {
        // 当schema有更新时才重新解析具体信息.
        if (!response.schema) {
            return response;
        }
        if (!this.details || response.update_dt !== this.get('update_dt')) {
            this.details = FG.parseDatasetSchema(response.schema, true);
            var tmpStr = JSON.stringify(this.details);
            tmpStr = tmpStr.replace(/"<%=/g, '<%=').replace(/%>"/g, '%>');
            //FG.print(tmpStr)
            this.detailsTemplate = _.template(tmpStr);
            this.templateList = [];
            //this.set('schema', response.schema);\

            if (_.has(this.details, 'global')) {
                var dsGlobal = this.details.global;
                if (_.has(dsGlobal, 'template_default')) {
                    _.extend(this.defaultData, dsGlobal.template_default);
                }
            }
        }
        return response;
    },

    // 获得DSV信息, 供pitem和widget使用.
    // 其参数args里应含data, 即用来替换模板的字典.
    getDSV: function(args) {
        var cleanedData = _.extend(_.clone(this.defaultData), args.data);
        var cleanedJSONs = {};
        _.each(cleanedData, function(value, key) {
            cleanedJSONs[key] = JSON.stringify(value)
        });
        var resJSON = this.detailsTemplate(cleanedJSONs);
        var res = JSON.parse(resJSON);
        // 将结果中不需要的subsets去掉
        if (_.has(cleanedData, 'subsets')) {
            res.subs = _.filter(res.subs, function(subset) {
                return _.contains(cleanedData.subsets, subset.name)
            })
        }

        return res;
    },

    // 获得单条数据所属的pack的uri
    getPackUri: function(datauri) {
        var uriInfo = FG.parseUriInfo(datauri);
        var resTokens = [uriInfo.dataset, uriInfo.subset];
        var theSubset = _.find(this.details.subs, function (subset){
            return (subset.name === uriInfo.subset);
        });
        var theV = _.find(theSubset.subs, function(v){
            return (v.name === uriInfo.varname)
        });
        if (!!theV.zipped) {
            resTokens.push('*')
        } else {
            resTokens.push(uriInfo.varname);
        }
        _.each(uriInfo.coords, function(coord, index){
            var c = theV.coords[index];
            if (!!c.zipped) {
                resTokens.push('*');
            } else {
                resTokens.push(coord);
            }
        });
        return resTokens.join('/');
    }
});

FG.DatasetCollection = Backbone.Collection.extend({
    url: '/api/fg/dataset',
    model: FG.Dataset
});

// 解析schema时处理special部分.
// 应当被bound在brief上.
FG.applyOrPassSpecial = function(content, key) {
    var parseSpecRule = /^([^:]*):([^:]*)$/;
    var parseRes = parseSpecRule.exec(key);
    if (parseRes.length > 1) {
        var arch = parseRes[1],
            name = parseRes[2];
        if (arch === this.arch) {
            // Apply here
            if (name === this.name) {
                _.extend(this, content);
            }
        } else {
            // Passing on
            if (_.has(this, 'subs')) {
                _.each(this.subs, function(sub) {
                    if (!_.has(sub, 'special')) {
                        sub.special = {};
                    }
                    var toPass = {};
                    toPass[key] = content;
                    sub.special = _.extend(toPass, sub.special);
                })
            }
        }
    }
    return this;
};

// 解析DatasetSchema
// 参数schema是待解析的对象.
// inplace如果为真, 则就地解析, 否则先将schema复制一份.
FG.parseDatasetSchema = function(schema, inplace) {
    var parsing = (!!inplace) ? schema : JSON.parse(JSON.stringify(schema));

    if (_.has(parsing, 'subs') || parsing.type === 'v+coords') {
        if (parsing.type === 'v+coords') {
            // 对于变量+坐标类型的特殊处理
            parsing.subs = [];
            var coords = parsing.subpack.coords;
            _.each(parsing.subpack.varnames, function (varname) {
                var variable = {
                    arch: 'varname',
                    name: varname,
                    coords: coords
                };
                if (!!parsing.subpack.varname_zipped) {
                    // 变量合并存储的情况
                    variable.zipped = true;
                }
                //if (!!brief.subpack.cocoords) {
                //    variable.cocoords = brief.subpack.cocoords;
                //}
                parsing.subs.push(variable);
            });
            delete parsing['subpack'];
            parsing.type = 'SV';
        }

        // 将common传递到下一层
        if (_.has(parsing, 'common')) {
            _.each(parsing.subs, function(sub){
                if (!_.has(sub, 'common')) {
                    sub.common = {};
                }
                sub.common = _.extend(_.clone(parsing.common), sub.common);
            });
            delete parsing['common'];
        }

        // 解析special, 将适用于本层的应用之, 将其余的传到下一层.
        if (_.has(parsing, 'special')) {
            _.each(parsing.special, FG.applyOrPassSpecial, parsing);
            delete parsing['special'];
        }

        // 继续解析下一层
        _.each(parsing.subs, function(sub) {
            FG.parseDatasetSchema(sub, true)
        });
    } else {
        // 将common应用到本层
        if (_.has(parsing, 'common')){
            var common = parsing.common;
            if (_.has(common, 'vartype') && parsing.arch === 'varname') {
                // 对于variable, common里可能会带有vartype和datatype两种类型标识, 而vartype应当被应用为variable的type.
                common['type'] = common.vartype;
                delete common['vartype'];
            }
            delete parsing['common'];
            _.each(['info', 'coords', 'data'], function(field) {
                if (_.has(common, field)){
                    parsing[field] = _.extend(_.clone(common[field]), parsing[field])
                }
            });
            //parsing = _.extend(_.clone(common), parsing);
        }
        // 应用special
        if (_.has(parsing, 'special')) {
            _.each(parsing.special, FG.applyOrPassSpecial, parsing);
            delete parsing['special'];
        }

        // 翻译:
        if (parsing.arch === 'varname') {
            if(_.has(parsing, 'varnamedict')) {
                parsing.zhname = parsing.varnamedict[parsing.name];
                delete parsing['varnamedict'];
            }

            if (_.has(parsing, 'unitsdict')) {
                parsing.units = parsing.unitsdict[parsing.name];
                delete parsing['unitsdict'];
            }

            if (_.has(parsing, 'coordnamedict')) {
                _.each(parsing.coords, function(coord) {
                    coord.zhname = parsing.coordnamedict[coord.name];
                });
                delete parsing['coordnamedict'];
            }
        }
    }

    return parsing;
};

/**
 * FG.Variable是包含配套信息的DSV之变量类型
 * @param options
 * @constructor
 */
FG.Variable = function(options) {
    // TODO: 待完善.
    var this_ = this;
    this.dataset = options.dataset;
    this.subset = options.subset;
    this.name = options.name;
    this.uri = options.uri;
    this.datasetObj = options.datasetObj;
    this.subsetObj = options.subsetObj;
    this.formats = options.formats;
    // TODO: coorder or simply coords?
    this.coorder = options.coorder;

    this.manager = options.manager;
    this._dataBundleDict = {};

    // ? getData or getDataPack?
    this.getDataBundle = function(args) {
        var this_ = this;
        var uris = this_.coorder.getReplacedUris();
        var results = _.map(uris, function(uri) {
            var datauri = this_.uri + '/' + uri;
            var dataunit = new FG.DataUnit({uri: datauri, src: this_.manager});
            return {tag: datauri, data: dataunit};
        });
        return new FG.DataBundle({items: results});
    };
};

// 用来根据由schema生成的dsv组成的列表, 生成供pitem管理的变量字典.
FG.genVariableDict = function(dsvList, packManager) {
    var variableDict = {};
    _.each(dsvList, function(dataset){
        _.each(dataset.subs, function(subset){
            _.each(subset.subs, function(variable){
                var dsvTag = [dataset.name, subset.name, variable.name].join('/');
                var coorder = new DX.Coorder();
                _.each(variable.coords, function(coord){
                    coorder.addCoord(new DX.Coord(coord))
                });

                variableDict[dsvTag] = new FG.Variable({
                    //variable: variable,
                    dataset: dataset.name,
                    subset: subset.name,
                    name: variable.name,
                    datasetObj: dataset,
                    subsetObj: subset,
                    uri: dsvTag,
                    formats: variable.formats,
                    coorder: coorder,
                    manager: packManager
                })
            })
        })
    });
    return variableDict;
};

/**
 * 数据包管理器
 * @param options
 * @constructor
 */
FG.PackManager = function(options) {
    this.options = _.extend({}, options);
    var this_ = this;

    // 成员
    this.datasets = {};
    this.datasetDfds = {};
    this.packs = {};
    this.records = {};  // key: [bundleName1, bundleName2, ...] pairs
    this._subsetDict = {};

    // 接口
    this.loadDetails = function(details, args) {
        _.each(details, function(dataset) {
            _.each(dataset.subs, function(subset) {
                _.each(subset.subs, function(v){
                    var vField = (!!v.zipped) ? '*' : v.name;
                    var dsvURIZipped = [dataset.name, subset.name, vField].join('/');
                    var dsvURI = [dataset.name, subset.name, v.name].join('/');

                    //var cFieldsList = [];
                    var coorder = new DX.Coorder();
                    _.each(v.coords, function(coord) {
                        var coordModel = new DX.Coord(coord);
                        if (!_.has(coord, 'depends')) {
                            var coord_ = JSON.parse(JSON.stringify(coord));
                            coordModel.setSelects(coord_.values);
                            //coord_.selects = coord_.values;
                            //cFieldsList.push(coord.values);
                        }
                        coorder.addCoord(coordModel);
                    });

                    var coordURIs = coorder.getReplacedUris();
                    var coordURIsZipped = coorder.getReplacedUris({zipped:true, zipped_repeat:true});
                    var i, fullURI, fullURIZipped;
                    for (i=0; i<coordURIs.length; i+=1) {
                        fullURI = dsvURI + '/' + coordURIs[i];
                        fullURIZipped = dsvURIZipped + '/' + coordURIsZipped[i];
                        // 生成ProductPack 并加上管理记录.
                        var pack;
                        if (!_.has(this_.packs, fullURIZipped)) {
                            pack = new FG.DataPack({uri: fullURIZipped});
                            this_.packs[fullURIZipped] = pack;
                        } else {
                            pack = this_.packs[fullURIZipped];
                        }
                        // 给pack自身的目录加上此条目.
                        pack.pushTOC(fullURI);

                        // 给manager本身加上索引记录.
                        if (!_.has(this_.records, fullURI)) {
                            this_.records[fullURI] = [];
                        }
                        this_.records[fullURI].push(fullURIZipped);

                        DX.warn(fullURI);
                        DX.print(fullURIZipped)
                    }
                })
            })
        })
    };

    this.addPack = function(pack) {
        this_.packs[pack.uri] = pack;
        _.each(pack.toc, function(contents, key) {
            this_.addToPool(key, pack.uri)
        })
    };

    this.removePack = function(packName) {
        var pack = this_.packs[packName];
        if (typeof pack == 'undefined') {
            return;
        }
        _.each(pack.toc, function(contents, key) {
            this_.removeFromPool(key, packName)
        });
        delete this_.packs[packName];
    };

    this.addToPool = function(key, packName) {
        // TODO: 当有多个bundle含有同样key的时候, 如何确保更精细的bundle的优先级更高.
        if (_.has(this_.records, key)) {
            if (!_.contains(this_.records[key], packName)) {
                this_.records[key].append(packName)
            }
        } else {
            this_.records[key] = [packName];
        }
    };

    this.removeFromPool = function(key, packName) {
        if (_.has(this_.records, key)) {
            this_.records[key] = _.without(this_.records[key], packName);
            if (this_.records[key].length === 0) {
                delete this_.records[key]
            }
        }
    };

    this.getKeys = function() {
        return _.keys(this_.records);
    };

    this.hasKey = function(key) {
        return _.has(this_.records, key);
    };

    this.getPack = function(key) {
        var bestPackName = this_.records[key] && _.last(this_.records[key]);
        if (typeof bestPackName === 'undefined') {
            return;
        } else {
            return this_.packs[bestPackName];
        }
    };

    this.getData = function(uri) {
        var uriInfo = FG.parseUriInfo(uri);
        var ds = uriInfo.dataset + '/' + uriInfo.subset;
        var dataset = this_._subsetDict[ds];

        var packUri = dataset.getPackUri(uri);
        var pack;
        if (!_.has(this_.packs, packUri)) {
            pack = new FG.DataPack({uri:packUri});
            //this.packs[packUri] = pack;
            this.addPack(pack);
        } else {
            pack = this_.packs[packUri];
        }
        return pack.getData(uri);
    };

    //this.genPacks = function(rule) {
    //
    //};
    // genPacks or genVars? or both?

    // returns a Deferred
    this.getDataset = function(datasetName) {
        //var this_ = this;
        var dfd;
        if (_.has(this.datasetDfds, datasetName)) {
            dfd = this.datasetDfds[datasetName];
            // dfd.resolve(this_.datasets[datasetName]);
        } else {
            dfd = $.Deferred();
            dfd.dataset = new FG.Dataset({id: datasetName});
            dfd.loading = false;
        }

        if (!!dfd.dataset.details) {
            dfd.resolve(dfd.dataset)
        } else if (!dfd.loading) {
            dfd.loading = true;
            dfd.dataset.fetch({
                success: function(){
                    var datasetUri = dfd.dataset.details.name;
                    _.each(dfd.dataset.details.subs, function(subset) {
                        var subsetUri = subset.name;
                        var dsUri = datasetUri + '/' + subsetUri;
                        this_._subsetDict[dsUri] = dfd.dataset;
                    });
                    dfd.resolve(dfd.dataset);
                    dfd.loading = false;
                },
                error: function() {
                    dfd.resolve(null);
                    dfd.loading = false;
                }
            })
        }
        return dfd;
    };
    
    this.loadDatasets = function() {
        var dsCol = new FG.DatasetCollection();
        dsCol.fetch({
            success: function(collection) {
                collection.each(function(d) {
                    var dsname = d.get('name'),
                        dfd = $.Deferred();
                    dfd.dataset = d;
                    dfd.loading = false;
                    this_.datasetDfds[dsname] = dfd;
                });
            }
        })
    }
};

/**
 * DataBundle是由若干DataUnit组成的数据捆, 主要是为了绘图方便
 * 其options.items是数据列表, 每一项是由以下成员组成的对象:
 *      tag: 标签名, 在画图时以此作为图例中的名字.
 *      data: DataUnit对象.
 * 其getData()函数返回一个延迟对象, 将被填充具有实际数据的类似items的[{tag:, data:}]列表. (另外接受单个或多个tags)
 * @param options
 * @constructor
 */
FG.DataBundle = function(options) {
    this.options = _.extend({items: []}, options);
    var this_ = this;

    this.items = this.options.items;


    // 可接受单个的tag或者多个tag
    this.getData = function(tags) {
        var selItems = this.items;
        if (_.isString(tags)) {
            var theItem = _.find(this_.items, function(item) {return item.tag === tags});
            if (!!theItem) {
                return theItem.data.getData();
            }
        } else if (_.isArray(tags)) {
            selItems = _.filter(this_.items, function(item) {return _.contains(tags, item.tag)});
        }

        //this.items
        var dfd = $.Deferred();
        var dataList = _.map(selItems, function(item) {
            return item.data.getData();
        });
        $.when.apply(this_, dataList).done(function() {
            var resList = [];
            var i;
            for (i=0; i < arguments.length; i += 1) {
                var res = {tag: selItems[i].tag, data: arguments[i]};
                resList.push(res);
            }
            dfd.resolve(resList);
        });
        return dfd;
    };
};

FG.parseUriInfo = function(uri) {
    var tokens = uri.split('/');
    var res = {};
    if (tokens.length >= 1) {
        res.dataset = tokens[0];
    }
    if (tokens.length >= 2) {
        res.subset = tokens[1];
    }
    if (tokens.length >= 3) {
        res.varname = tokens[2];
    }
    if (tokens.length >= 4){
        res.coords = tokens.slice(3);
    } else {
        res.coords = [];
    }
    return res;
};

FG.simplifyTags = function(bundleItems, opts) {
    opts = _.extend({
        inplace: false
    }, opts);
    var splits = _.map(bundleItems, function(item){
        var tokens = item.tag.split('/');
        return {item: item, rawTag: item.tag, tokens: tokens, length: tokens.length};
    });
    var common,
        shortTags;
    if (splits.length == 0) {
        return {commonTag: '', shortTags: []};
    } else if (splits.length == 1) {
        if (opts.inplace) {
            bundleItems.commonTag = null;
        }
        return {commonTag: null, shortTags: [splits[0].rawTag]};
    }

    var lengths = _.pluck(splits, 'length'),
        length0 = lengths[0];

    if (!_.every(lengths, function(l) {return l === length0})) {
        common = '';
        shortTags = _.pluck(splits, 'rawTag');
    } else {

        var sameParts = [],
            diffParts = [];
        for (var i=0; i<length0; i++){
            var pool = [];
            for (var j=0; j<splits.length; j++) {
                pool.push(splits[j].tokens[i]);
            }
            if (_.every(pool, function(tk) {return tk === pool[0]})) {
                sameParts.push(pool[0])
            } else {
                sameParts.push('_');
                diffParts.push(_.clone(pool));
            }
        }
        common = sameParts.join('/');
        shortTags = _.map(_.zip.apply(null, diffParts), function(parts) {return parts.join(' ')});
    }
    if (opts.inplace) {
        _.each(bundleItems, function(item, i) {
            item.tag = shortTags[i]
        });
        bundleItems.commonTag = common;
    }
    return {commonTag: common, shortTags: shortTags};
};

FG._sysPackManager = undefined;
FG.getPackManager = function() {
    if (typeof FG._sysPackManager === 'undefined') {
        FG._sysPackManager = new FG.PackManager();
        FG._sysPackManager.loadDatasets();
    }
    return FG._sysPackManager;
};